Hello! This is the second R notebook on working with climate data. In the last notebook, we introduced the programming constructs of a “variable” and a “function”. We then worked with 1 and 2 dimensional ascii data to make some scatter plots. In this notebook, we will introduce new functions for opening and working with a type of file called a NetCDF file, which contains 3 dimensional and higher dimensional data. But don’t panic, we will stil just be storing this new data in variables, and working with the data with functions, so you already know all the theory. Let’s get started!

3 dimensional climate data - NetCDF files.

As mentioned in the last R notebook, the first thing we always need to do is set our working directory to the folder where we have saved the data. Otherwise R doesn’t know where to look for the data. We run the code in the gray code chunk below by clicking in the gray box and pressing Ctrl+Enter (assuming you have opened this document in R studio).

You probably havn’t heard of NetCDF as a file type. NetCDF files are files that normally end with the file extension .nc or sometimes .grd. NetCDF files are used for various types of climate data and geophysical data. The structure of data inside a NetCDF file is like a stack of spreadsheets ontop of each other, where the x and y variables are normally lattitude and longitude. See the picture below. The structure of a netcdf file. The X and Y variable refer to Lattitude and Longitude for climate data, although not always. Often a NetCDF file contains data for the whole world, but they can just contain data for a part of the world. The random numbers in each square in the diagram represent the actual data for the variable of interest - this could be mean annual temperature, or rainfall, or ozone concentration, or anything. The time axis doesn’t always exist in a netcdf file. If the time axis doesn’t exist, the structure of the file is like just the first orange layer in the netcdf diagram above, without the pink and green layers.

To work with netcdf files in R, you need to install a couple of packages. R is such a popular language for science and data analysis because it has thousands of packages which can be downloaded (for free!) with a single line of code. These packages range from forecasting time series for weather analysis or stock prices, to working with geospatial data, to higher performance parallelisation routines, to adding pictures of cats to plots (yes, really!) to….. you get the idea. If you can think of a scientific problem, there’s probably already a package for it in R.

In R, you install a package once using the “install.package()” function. This normally only needs to be done once on a computer. Then the package must be loaded into R with the “library()” function. This needs to be done every time you restart R. Let’s load the ncdf4 package.

# Install the "ncdf4" package from the internet. This normally only needs to be done a single time on a computer, and then it is installed forever. 
install.packages("ncdf4")

You should see a load of both black and red text flash by on the screen. The important bit is the last few lines, which should say thing have been loaded ok. Specifically, the final line should say “* DONE ([the package name])”. The package has now been installed, but it still needs to be loaded into R (and loading the package needs to be done at the start of every script with a package in it). Let’s do this now.

# Load the "ncdf4" package into R. This needs to be done at the start of every R script that uses the package. 
library("ncdf4")

You shouldn’t see any output from running this line of code, but we can now use the functions in the ncdf4 package.

What we are going to walk you through doing in the next few code chunks is the standard workflow for using a NetCDF file in R. This workflow is as follows: 1. Open the NetCDF file connection in R with the “nc_open()” function 2. Find out what is actually inside the netcdf file with the “print()” function 3. Extract the variables we want to use from the NetCDF file using the “ncvar_get()” function 4. Extract any attributes we want from the NetCDF file using the “ncatt_get()” function 5. Close the NetCDF file connection in R with the “nc_close()” function.

Each of the steps above is quite easy, so don’t panic!

First, we want to open the NetCDF file and look at the ranges of different variables. Luckily this is quite easy in R. First we use the ncdf4 library to open a connection to the file and store this connection in a variable, and then we use the print function to print a summary of the file. Lets do this now. Remember if you havn’t run the install.packages() and the library() functions, then R will complain that it “can’t find” the function - which means R doesn’t know what these functions are because they havn’t been loaded yet.

# Open a connection to the NetCDF file and store this connection in a variable called ncfile. (don't worry about what we mean by a "connection" to the file, this will become clear throughout the examples.)
ncfile <- nc_open('2016722131556EnsembleGPP_MR_1deg.nc')
# Print the header of the NetCDF file (i.e. print the NetCDF file's metadata)
print(ncfile)
File 2016722131556EnsembleGPP_MR_1deg.nc (NC_FORMAT_CLASSIC):

     3 variables (excluding dimension variables):
        double time_bnds[bnds,time]   
        float gpp[lon,lat,time]   
            standard_name: gross_primary_productivity_of_carbon
            long_name: Carbon Mass Flux out of Atmosphere due to Gross Primary Production on Land
            units: kg m-2 s-1
            _FillValue: -9999
            missing_value: -9999
            original_name: EnsembleGPP_MR_May12
            cell_methods: time: mean area: mean where land
            history: generated 05/2012
            associated_files: none
        float std[lon,lat,time]   
            standard_name: uncertainty based on spread of ensemble
            long_name: standard deviation calculated based on median absolute deviation
            units: kg m-2 s-1
            _FillValue: -9999
            missing_value: -9999
            original_name: std
            cell_methods: time: mean area: mean where land
            history: generated 05/2012
            associated_files: none

     4 dimensions:
        time  Size:360   *** is unlimited ***
            standard_name: time
            bounds: time_bnds
            units: days since 1582-10-14 00:00:00
            calendar: standard
            axis: T
        bnds  Size:2
        lon  Size:360
            standard_name: longitude
            long_name: longitude
            units: degrees_east
            axis: X
        lat  Size:180
            standard_name: latitude
            long_name: latitude
            units: degrees_north
            axis: Y

    13 global attributes:
        CDI: Climate Data Interface version 1.9.3 (http://mpimet.mpg.de/cdi)
        Conventions: CF-1.6
        history: Mon Mar 26 10:01:10 2018: cdo remapbil,./1.grid.asc -setgrid,05.grid.asc 2016722131556EnsembleGPP_MR.nc 2016722131556EnsembleGPP_MR_1deg.nc
Mon Feb 11 10:28:28 2013: cdo copy EnsembleGPP_MR_May12.1982.nc EnsembleGPP_MR_May12.1983.nc EnsembleGPP_MR_May12.1984.nc EnsembleGPP_MR_May12.1985.nc EnsembleGPP_MR_May12.1986.nc EnsembleGPP_MR_May12.1987.nc EnsembleGPP_MR_May12.1988.nc EnsembleGPP_MR_May12.1989.nc EnsembleGPP_MR_May12.1990.nc EnsembleGPP_MR_May12.1991.nc EnsembleGPP_MR_May12.1992.nc EnsembleGPP_MR_May12.1993.nc EnsembleGPP_MR_May12.1994.nc EnsembleGPP_MR_May12.1995.nc EnsembleGPP_MR_May12.1996.nc EnsembleGPP_MR_May12.1997.nc EnsembleGPP_MR_May12.1998.nc EnsembleGPP_MR_May12.1999.nc EnsembleGPP_MR_May12.2000.nc EnsembleGPP_MR_May12.2001.nc EnsembleGPP_MR_May12.2002.nc EnsembleGPP_MR_May12.2003.nc EnsembleGPP_MR_May12.2004.nc EnsembleGPP_MR_May12.2005.nc EnsembleGPP_MR_May12.2006.nc EnsembleGPP_MR_May12.2007.nc EnsembleGPP_MR_May12.2008.nc EnsembleGPP_MR_May12.2009.nc EnsembleGPP_MR_May12.2010.nc EnsembleGPP_MR_May12.2011.nc EnsembleGPP_MR.nc
generated 05/2012
        institution: Max Planck Institude for BioGeoChemistry Jena, Germany
        institute_id: MPI-BGC
        experiment_id: may12
        model_id: may12
        forcing: 1982-1997 GIMMS 1998-2005 SeaWIFS 2006-2011 MERIS, ERAinterim meteo input
        contact: mjung@bgc-jena.mpg.de
        references: Jung, M., Reichstein, M., Bondeau, A. 2009 Biogeosciences, 6;    Reichstein, M., et al. (2005), Global Change Biology, 11: 1424–1439.
        title: Gross Primary Production on Land
        description: GPP derived by upscaling observations from the current global network of eddy-covariance towers (FLUXNET, Jung et al 2011 Journal of Geophysical Research, 116 G00J07). For the upscaling a model tree ensemble approach was used as described in (Jung, M., Reichstein, M., Bondeau, A. 2009 Biogeosciences, 6). For this dataset the flux partitioning was based on Reichstein, M., et al. (2005), Global Change Biology, 11: 1424–1439
        CDO: Climate Data Operators version 1.9.3 (http://mpimet.mpg.de/cdo)

Ouch, that’s quite a lot of text! Take a moment to read it all. Even though the output looks confusing, this is the same sort of output you will get from every NetCDF file you open with R, so it will become easy after you’ve done it a couple of times.

The first few lines of the output say: > 3 variables (excluding dimension variables): double time_bnds[bnds,time] float gpp[lon,lat,time] (some other output) float std[lon,lat,time] (some other output)

This tells us there are 3 variables in this netcdf file. The first variable is time_bnds, the second is gpp, and the third is std. You will often see “double” and “float”. This refers to how many decimal places the computer is storing numbers as. The important bit is that it is just a normal decimal number.

The next paragraph of the print() output tells us information about the variables in the NetCDF file:

4 dimensions:

time Size:360 (some other output) bnds Size:2 (some other output) lon Size:360 (some other output) lat Size:180 (some other output)

This tells us that the dimensions of the file are lattitude (stored in the variable called “lat”), longitude (stored in the variable called “lon”), and time (stored in the variable called “time”). There is a forth variable called bnds - don’t worry about this, we dont need it.

After this, we have a long paragraph which starts with

13 global attributes: (loads of output)

In a NetCDF file, the global attributes are normally useful things like who made the file, when it was made, a description, and other useful information which is often called meta-data.

We now understand a bit about our NetCDF file. The next step is to extract variables in the NetCDF file into variables in R. To do this we use the “ncvar_get()” function. The function arguments nc= refers to the NetCdf file, while the varid= function argument refers to the variable name in the NetCDF file. This is why we needed to use the print() function in the last step; to find out the variable names.

# Extract the 'lat' variable in the netcdf file, and store it in a variable called 'lat' in R.
lat=ncvar_get(nc=ncfile, varid='lat')
# Extract the 'lon' variable in the netcdf file, and store it in a variable called 'lon' in R.
lon=ncvar_get(nc=ncfile,varid='lon')
# Extract the 'time' variable in the netcdf file, and store it in a variable called 'time' in R.
time=ncvar_get(nc=ncfile, varid="time")
# Extract the 'gpp' variable in the netcdf file, and store it in a variable called 'gpp' in R.
gpp = ncvar_get(nc=ncfile, varid='gpp')

We now have 4 variables in R holding the information we extracted from the variables inside the NetCDF file. We want to know what is inside these variables. We could just print their whole contents to the screen by just running their name in the usual way. However we know that some of these variables might be very, very big, and this might cause the computer to crash. A safer way is to use the “length()” function or the “dim()” function to see how long the variable is. Lets do this:

length(lat)
[1] 180
length(lon)
[1] 360
length(time)
[1] 360
length(gpp)
[1] 23328000

Ok, so that final variable gpp has 23 million entries. Probably a good thing we didn’t just print that to the screen. (Also, if you’re still thinking why are we using R not excel, try opening a 23 million line long file in excel). R has two useful functions, “head()” and “tail()”, for looking at the first few or last few entires of a really long file without printing everything. Lets have a quick look at the first few values of each variable.

# Use the head() function to see the first 5 entries of each variable, to help us understand out data. 
head(lat)
[1] -90 -89 -88 -87 -86 -85
head(lon)
[1] -180 -179 -178 -177 -176 -175
head(time)
[1] 145801 145832 145860 145891 145921 145952
head(gpp)
[1] NA NA NA NA NA NA

So it looks like the latitude variable goes from -90 upwards, i.e. from the south pole. That’s fine. The longitude variable seems to go from -180 upwards, which sounds about right too. The time variable seems to be a bit crazy though! We should look back at the meta data from the print() function. If you look back, we find out that the time variable is in units of “days since 1582-10-14 00:00:00”. Ok, so we are probably looking for a big number then. The last variable gpp seems to have lots of NA’s (standing for not applicable). This might mean we have loaded it incorrectly, or it might just be because there is no data at that point. Lets look at the end of the variable:

tail(gpp)
[1] NA NA NA NA NA NA

Hmm. The end of the variable also seems to be filled with NAs. We might now be slightly worried about whether all of the gpp variable is full of NAs. A quick way to check this without reading all 23 million values is to sum up the whole vector using the “sum()” function and see if it comes to a value greater than 0. Note you need the na.rm=TRUE function argument, which tells R to remove the NAs before summing, otherwise R will return NA.

sum(as.vector(gpp), na.rm=T)
[1] 0.1351143

Ok, the value is above 1. So we do have data in the gpp variable. It’s ok.

The last thing we want to do is extract some of the attributes in the meta-data. Lets get the full name of the gpp variable and it’s units. We can then use these for things like plot titles later. Let’s also get the _FillValue. This is the value in the NetCDF file that corresponds to NA. The fact that we saw lots of NAs with the head and tail function might show that R has managed to handle it for us already, but it’s better to be safe than sorry!

# Get the long name attribute for the gpp variable, for plotting later
gpp_name=ncatt_get(ncfile, "gpp", "long_name")
print(gpp_name)
$hasatt
[1] TRUE

$value
[1] "Carbon Mass Flux out of Atmosphere due to Gross Primary Production on Land"
# Get the units of the gpp variable, for plotting later 
gpp_units=ncatt_get(ncfile, "gpp", "units")
print(gpp_units)
$hasatt
[1] TRUE

$value
[1] "kg m-2 s-1"
# Get the value that corresponds to NA in the gpp variable. 
gpp_fillvalue <- ncatt_get(ncfile,"gpp","_FillValue")
print(gpp_fillvalue)
$hasatt
[1] TRUE

$value
[1] -9999

From printing these, we can see that each extracted attribute has an “hasatt” and a “value”. hasatt stands for “has attribute” and is either true or false. If it is true, the “value” holds the value.

We have now extracted all the data we need from the NetCDF file connection. We can now close the connection to the file. This frees up some computer memory for us. Note that we didn’t need to do this in the last notebook for the “read.csv()” and “read.table()” functions, which close the data file automatically.

nc_close(ncfile)

Now for a super useful little line of code. NetCDF files often have NA values that correspond to a very big or very samll number. For example, a common number to represent NA is -9999.99. If we try to plot the gpp variable in R, the colour scale will expand to -9999.99 and so the map will be impossible to understand. Instead we want to set these -9999.99 values (or whatever the NetCDF’s _FillValue is) to NA inside R. R will then not plot anything which is NA, and our plots will be human readable.

The line of code below is short, but quite complex. What the line of code does, is use the square brackets notation to extract any values in the gpp variable which equal the value in gpp_fillvalues, and set those extracted values to NA. If you need to use this line of code in your own work, just copy and paste and change the variable names to the variable names in your work.

# replace netCDF fill values with NA's
gpp[gpp==gpp_fillvalue$value] = NA

Now that we have extracted all the data in our NetCDF file, we are ready to start making some maps of the data!

Making our first map

The data inside a NetCDF file (and therefore the data in our variables in R which we have extracted from the NetCDF files) is a rectangular grid of numbers. In fact, if we have a time variable, then it is a 3D cube of data - imagine a rubix cube of data, but with the dimensions of our data instead of the normal 3x3x3 rubix cube.

Remember we extracted four data variables from the NetCDF file with the ncvar_get() function. Those variables were lat, lon, time, and gpp.

Let’s remind ourselves of the dimensions of our data:

dim(gpp)
[1] 360 180 360

So 360 in the x dimension, 180 in the y dimension, and 360 in the z dimension. If we check back to the length of the variables from the print() function we can see this corresponds to lat, long and time.

This is just a matrix of data, so we can extract any subset of it using the square bracket notation. Let’s extract the first time slice of the data, and save it in a variable called first_gpp_slice.

first_gpp_slice = gpp[,,1]
image(first_gpp_slice)

Remember using the square bracket notation to slice data in the last R notebook? In that notebook, use said how for 2D data the format is some_variable[x_dimension,y_dimension]. This means to select the first row of the first column would be some_variable[1,1]. In that notebook we also mentioned that if we leave the places before or after the comma inside the square brackets blank (like this: some_variable[,1] or some_variable[1,]) that we select all of that column or all of that row.

Hopefully that is not too confusing so far. So we know our gpp variable contains lattitude x longitude x time. Therefore running gpp[,,1] means give me all lattitude and all longitude for the first time slice only.

Knowing all of this, if we run our trusty dim() function on our new variable first_gpp_slice, we should get results of 360 x 180 x 1, as we took all longtude and all lattitude, at the first time only (not at all time slices). So essentially we took the first layer off our rubix cube.

Lets check we get 360 x 180 x 1 with our dim() function.

dim(first_gpp_slice)
[1] 360 180

Ok, so we get 360 x 180, which is the same as 360 x 180 x 1. So we now have a single time slice stored in a matrix. We can easily make a picture from a matrix using the image function:

Cool! But there is an obvious problem - the world seems to be upside down. Luckily this is easily fixed. We just slightly modify how we are slicing the gpp variable. You see, when we write gpp[,,1] to represent “give me all the lattitude and all the longitude values for the first time slice”, under the hood R is treating this the same as if we had written gpp[ 1:360 , 1:180 , 1 ]. Therefore to flip the matrix in the lattitude direction, we just change the 1:180 to 180:1 to reverse it. See below:

Ok, now we’re cooking! This is a map of the first time slice of our gpp variable! But for it to be a decent map, we need a couple of things. First, it would be best to add a scale. It looks like the axes are currently just going from 0 to 1 as well - we probably want to change that. It might be useful to be able to change colour scales too. And maybe a title would be a good idea too. Let’s set about modifying our plot.

Adding a legend

So it turns out we have an immediate problem. Adding a legend to the image() function is really hard. Doh! The image function is still really useful as a quick first look at a matrix of numbers though. However it just so happens that in the fields package, some kind R programmer has modified the image() function into a new function called image.plot(), which has an automatic colour bar. It also allows us to specify the labels of the x and y axis, so we can actually display lattitude and longitude rather than the strange 0 to 1 scale. Uncomment the install.packages line underneath to install the fields package, then run the library command to load the package and the make the map:

Cool! Looking better already. There is a useful function called map() in R which allows us to add country outlines. This is often a good idea to add, as it shows there is no data at all for Antarctica, Greenland, and the Sahara. Note the add=T (short for add=TRUE), which adds to the current plot. Try changing some of the function arguments to customise the plot.

image.plot(lon, lat, flipped_first_gpp_slice)
map(database = 'world', lwd=1.5, add = T, col='black')

Changing colour pallets

There are a number of standard color palettes in R. The rainbow colour pallete is standard in image.plot(). However a number of recent scientific articles have pointed out that rainbow colour palletes can draw the eye to unimporant features as some color changes act like contours differently to different people. Therefore simpler colour pallets can often be better for displaying scientific data. Let’s install the RColorBrewer package to give us access to a load of useful color palletes. The RColorBrewer packages gives us access to the brewer.pal() function. This allows us to give a number of breaks in the colour pallete we want (normally up to 9 or 10, depending on the pallete) as the first function argument, and the name of the colour pallete as the second argument. So brewer.pal(10, “RdBu”) gives us a color pallete from red to blue with 10 breaks in it. This is cool, but we know low numbers for our gpp data are normally displayed with cold colours in environmental sciences, so we use the rev() function to reverse the order, making us a blue to red colour pallete. See below:

# install.packages("RColorBrewer")
library("RColorBrewer")
image.plot(lon, lat, flipped_first_gpp_slice, col = rev(brewer.pal(10, "RdBu")))
map(database = 'world', add = T, lwd=1.5)

You can find more about what color palletes are found in the RColorBrewer on this link . Let’s try the yellow-green-blue pallete instead, just for practise:

image.plot(lon, lat, flipped_first_gpp_slice, col = rev(brewer.pal(9,"YlGnBu")))
map(database = 'world', add = T, lwd=1.5)

Ok, now lets make the plot itself a bit better. Lets add a title, and remove the x and y labels using the main=, xlab= and ylab= function arguments we have already learnt. We can use our gpp_name variable that we extracted from the NetCDF file near the top of this script, and access it’s contents by calling gpp_name$value. If all of this is seeming like an overwhelming amount to remember, bear in mind you can just copy and paste code from this document and just modify names. You don’t have to remember all of this. There is no exam on R syntax :)

If you change just these arguments and run the code you will find half the title has been chopped off the side of the plot! Aggghhh! This is because the title is quite long, and R by default has a fixed amount of padding around plots which our long title runs over. This is rarely a problem, but this is one of those exceptions! We can fix this by calling the par() function (short for plot PARameters) and change the mar=c() argument (short for MARgins). The default for this is par(mar=c(5.1, 4.1, 4.1, 2.1)). This is for graphic design reasons that I imagine someone spent ages deciding on. The numbers refer to the bottom, left, top and right hand margins respectively. Therefore changing the second or fouth number to a smaller value should fix our problem.

Finally, we want to add the units of our plot to the legend. To do this I googled the image.plot function and found this page which tells us that the legend.lab= argument allows us to specify the text, the legend.line= argument moves the legend text to the side so it is not underneath the legend labels, and the legend.mar= expands the legend margin. Try changing the values and rerunning the code block several times to get a feeling for what each argument does.

par(mar=c(5.1,3,4.1,3))
image.plot(lon, lat, flipped_first_gpp_slice, col = rev(brewer.pal(9,"YlGnBu")), xlab="", ylab="", main=gpp_name$value, legend.lab=gpp_units$value, legend.line=4, legend.mar=7)
map(database = 'world', add = T, lwd=1.5)

Colourblind friendly / printing in black and white friendly colors?

A final quick note on a set of colour palletes found in Python and Matlab called Viridis, Inferno, Magma and Cubhelix, which have become popular as they are colourblind friendly, black and white printer friendly (try printing a rainbow colour pallete in black and white - there ends up being several dark bands, making the colourmaps unusable in black and white) and perceptual (i.e. they don’t have the random contouring effect in different peoples vision emphasizing different things). These are avaliable in the viridis package. Check out this link for more on how to use the package , and see below for an example of using it:

# install.packages("viridis")
library("viridis")
Loading required package: viridisLite
par(mar=c(3,3,3,3))
image.plot(lon, lat, flipped_first_gpp_slice, col=viridis(256), xlab="", ylab="", main=gpp_name$value, legend.lab=gpp_units$value, legend.line=4, legend.mar=7)
map(database = 'world', add = T, lwd=1.5)

Saving plots

Finally, you might well want to save the plots for later use, use in reports or presentations, or whatever. Any plot (e.g. scatter graph, pie chart, map, etc) can be saved in R.

Saving plots in R requires 2 extra lines of code. First, you open a graphic device using one of the following functions: png(), jpep(), pdf(), tiff(). After this, you run your normal plotting code. Then after your plotting code, add the line dev.off() to close the graphics device.

Note that if you save plots as a pdf, you can then open them in Inkscape or Adobe Illustrator to modify further.

png("gpp_map.png", width=10, height=5, units = 'in', res = 300)
par(mar=c(3,3,3,3))
image.plot(lon, lat, flipped_first_gpp_slice, col=plasma(256), xlab="", ylab="", main=gpp_name$value, legend.lab=gpp_units$value, legend.line=4, legend.mar=7)
map(database = 'world', add = T, lwd=1.5)
dev.off()
null device 
          1 

Note you need to run the whole chunk with Ctrl+Shift+Enter, Running it line by line with Ctrl+Enter will cause problems. After running this code, you should get an output saying null device 1 which means it has sucessfully closed the graphics device. You should then have a plot saved in your working directory.

End of Notebook 2

Well done for reaching the end of Notebook 2. I’m genuinely proud of you - that was a lot of code. But you got through it, and if you understood even half of it you can now do a huge amount. As with the last R notebook, I recommend going through this code at least one more time and possibly several times. And definitely start trying to play with the code. Change bits on each line to see what happens. Get used to R throwing error messages, and get used to googling how to do things. From reading these guides you will now have a good idea of the sorts of things you can do with R.

knitr::purl("Rmd_script_2_NetCDF.Rmd")
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAyIC0gd29ya2luZyB3aXRoIE5ldENERiBkYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpIZWxsbyEgVGhpcyBpcyB0aGUgc2Vjb25kIFIgbm90ZWJvb2sgb24gd29ya2luZyB3aXRoIGNsaW1hdGUgZGF0YS4gSW4gdGhlIGxhc3Qgbm90ZWJvb2ssIHdlIGludHJvZHVjZWQgdGhlIHByb2dyYW1taW5nIGNvbnN0cnVjdHMgb2YgYSAidmFyaWFibGUiIGFuZCBhICJmdW5jdGlvbiIuIFdlIHRoZW4gd29ya2VkIHdpdGggMSBhbmQgMiBkaW1lbnNpb25hbCBhc2NpaSBkYXRhIHRvIG1ha2Ugc29tZSBzY2F0dGVyIHBsb3RzLiBJbiB0aGlzIG5vdGVib29rLCB3ZSB3aWxsIGludHJvZHVjZSBuZXcgZnVuY3Rpb25zIGZvciBvcGVuaW5nIGFuZCB3b3JraW5nIHdpdGggYSB0eXBlIG9mIGZpbGUgY2FsbGVkIGEgTmV0Q0RGIGZpbGUsIHdoaWNoIGNvbnRhaW5zIDMgZGltZW5zaW9uYWwgYW5kIGhpZ2hlciBkaW1lbnNpb25hbCBkYXRhLiBCdXQgZG9uJ3QgcGFuaWMsIHdlIHdpbGwgc3RpbCBqdXN0IGJlIHN0b3JpbmcgdGhpcyBuZXcgZGF0YSBpbiB2YXJpYWJsZXMsIGFuZCB3b3JraW5nIHdpdGggdGhlIGRhdGEgd2l0aCBmdW5jdGlvbnMsIHNvIHlvdSBhbHJlYWR5IGtub3cgYWxsIHRoZSB0aGVvcnkuIExldCdzIGdldCBzdGFydGVkISAKCgojIDMgZGltZW5zaW9uYWwgY2xpbWF0ZSBkYXRhIC0gTmV0Q0RGIGZpbGVzLgoKQXMgbWVudGlvbmVkIGluIHRoZSBsYXN0IFIgbm90ZWJvb2ssIHRoZSBmaXJzdCB0aGluZyB3ZSBhbHdheXMgbmVlZCB0byBkbyBpcyBzZXQgb3VyIHdvcmtpbmcgZGlyZWN0b3J5IHRvIHRoZSBmb2xkZXIgd2hlcmUgd2UgaGF2ZSBzYXZlZCB0aGUgZGF0YS4gT3RoZXJ3aXNlIFIgZG9lc24ndCBrbm93IHdoZXJlIHRvIGxvb2sgZm9yIHRoZSBkYXRhLiBXZSBydW4gdGhlIGNvZGUgaW4gdGhlIGdyYXkgY29kZSBjaHVuayBiZWxvdyBieSBjbGlja2luZyBpbiB0aGUgZ3JheSBib3ggYW5kIHByZXNzaW5nICpDdHJsK0VudGVyKiAoYXNzdW1pbmcgeW91IGhhdmUgb3BlbmVkIHRoaXMgZG9jdW1lbnQgaW4gUiBzdHVkaW8pLiAKCmBgYHtyfQojIFNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgdG8gdGhlIGZvbGRlciB3aGVyZSB0aGUgZGF0YSBpcyBzYXZlZC4Kc2V0d2QoIn4vRG9jdW1lbnRzL3NjcmF0Y2gvUl9uZXRjZGYiKQpgYGAKCllvdSBwcm9iYWJseSBoYXZuJ3QgaGVhcmQgb2YgTmV0Q0RGIGFzIGEgZmlsZSB0eXBlLiBOZXRDREYgZmlsZXMgYXJlIGZpbGVzIHRoYXQgbm9ybWFsbHkgZW5kIHdpdGggdGhlIGZpbGUgZXh0ZW5zaW9uIC5uYyBvciBzb21ldGltZXMgLmdyZC4gTmV0Q0RGIGZpbGVzIGFyZSB1c2VkIGZvciB2YXJpb3VzIHR5cGVzIG9mIGNsaW1hdGUgZGF0YSBhbmQgZ2VvcGh5c2ljYWwgZGF0YS4gVGhlIHN0cnVjdHVyZSBvZiBkYXRhIGluc2lkZSBhIE5ldENERiBmaWxlIGlzIGxpa2UgYSBzdGFjayBvZiBzcHJlYWRzaGVldHMgb250b3Agb2YgZWFjaCBvdGhlciwgd2hlcmUgdGhlIHggYW5kIHkgdmFyaWFibGVzIGFyZSBub3JtYWxseSBsYXR0aXR1ZGUgYW5kIGxvbmdpdHVkZS4gU2VlIHRoZSBwaWN0dXJlIGJlbG93LiAKIVtUaGUgc3RydWN0dXJlIG9mIGEgbmV0Y2RmIGZpbGUuXShuZXRjZGYuZ2lmKQpUaGUgWCBhbmQgWSB2YXJpYWJsZSByZWZlciB0byBMYXR0aXR1ZGUgYW5kIExvbmdpdHVkZSBmb3IgY2xpbWF0ZSBkYXRhLCBhbHRob3VnaCBub3QgYWx3YXlzLiBPZnRlbiBhIE5ldENERiBmaWxlIGNvbnRhaW5zIGRhdGEgZm9yIHRoZSB3aG9sZSB3b3JsZCwgYnV0IHRoZXkgY2FuIGp1c3QgY29udGFpbiBkYXRhIGZvciBhIHBhcnQgb2YgdGhlIHdvcmxkLiBUaGUgcmFuZG9tIG51bWJlcnMgaW4gZWFjaCBzcXVhcmUgaW4gdGhlIGRpYWdyYW0gcmVwcmVzZW50IHRoZSBhY3R1YWwgZGF0YSBmb3IgdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0IC0gdGhpcyBjb3VsZCBiZSBtZWFuIGFubnVhbCB0ZW1wZXJhdHVyZSwgb3IgcmFpbmZhbGwsIG9yIG96b25lIGNvbmNlbnRyYXRpb24sIG9yIGFueXRoaW5nLiBUaGUgdGltZSBheGlzIGRvZXNuJ3QgYWx3YXlzIGV4aXN0IGluIGEgbmV0Y2RmIGZpbGUuIElmIHRoZSB0aW1lIGF4aXMgZG9lc24ndCBleGlzdCwgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZmlsZSBpcyBsaWtlIGp1c3QgdGhlIGZpcnN0IG9yYW5nZSBsYXllciBpbiB0aGUgbmV0Y2RmIGRpYWdyYW0gYWJvdmUsIHdpdGhvdXQgdGhlIHBpbmsgYW5kIGdyZWVuIGxheWVycy4KClRvIHdvcmsgd2l0aCBuZXRjZGYgZmlsZXMgaW4gUiwgeW91IG5lZWQgdG8gaW5zdGFsbCBhIGNvdXBsZSBvZiBwYWNrYWdlcy4gUiBpcyBzdWNoIGEgcG9wdWxhciBsYW5ndWFnZSBmb3Igc2NpZW5jZSBhbmQgZGF0YSBhbmFseXNpcyBiZWNhdXNlIGl0IGhhcyB0aG91c2FuZHMgb2YgcGFja2FnZXMgd2hpY2ggY2FuIGJlIGRvd25sb2FkZWQgKGZvciBmcmVlISkgd2l0aCBhIHNpbmdsZSBsaW5lIG9mIGNvZGUuIFRoZXNlIHBhY2thZ2VzIHJhbmdlIGZyb20gZm9yZWNhc3RpbmcgdGltZSBzZXJpZXMgZm9yIHdlYXRoZXIgYW5hbHlzaXMgb3Igc3RvY2sgcHJpY2VzLCB0byB3b3JraW5nIHdpdGggZ2Vvc3BhdGlhbCBkYXRhLCB0byBoaWdoZXIgcGVyZm9ybWFuY2UgcGFyYWxsZWxpc2F0aW9uIHJvdXRpbmVzLCB0byBhZGRpbmcgcGljdHVyZXMgb2YgY2F0cyB0byBwbG90cyAoeWVzLCByZWFsbHkhKSB0by4uLi4uIHlvdSBnZXQgdGhlIGlkZWEuIElmIHlvdSBjYW4gdGhpbmsgb2YgYSBzY2llbnRpZmljIHByb2JsZW0sIHRoZXJlJ3MgcHJvYmFibHkgYWxyZWFkeSBhIHBhY2thZ2UgZm9yIGl0IGluIFIuIAoKSW4gUiwgeW91IGluc3RhbGwgYSBwYWNrYWdlIG9uY2UgdXNpbmcgdGhlICJpbnN0YWxsLnBhY2thZ2UoKSIgZnVuY3Rpb24uIFRoaXMgbm9ybWFsbHkgb25seSBuZWVkcyB0byBiZSBkb25lIG9uY2Ugb24gYSBjb21wdXRlci4gVGhlbiB0aGUgcGFja2FnZSBtdXN0IGJlIGxvYWRlZCBpbnRvIFIgd2l0aCB0aGUgImxpYnJhcnkoKSIgZnVuY3Rpb24uIFRoaXMgbmVlZHMgdG8gYmUgZG9uZSBldmVyeSB0aW1lIHlvdSByZXN0YXJ0IFIuIExldCdzIGxvYWQgdGhlIG5jZGY0IHBhY2thZ2UuIAoKYGBge3J9CiMgSW5zdGFsbCB0aGUgIm5jZGY0IiBwYWNrYWdlIGZyb20gdGhlIGludGVybmV0LiBUaGlzIG5vcm1hbGx5IG9ubHkgbmVlZHMgdG8gYmUgZG9uZSBhIHNpbmdsZSB0aW1lIG9uIGEgY29tcHV0ZXIsIGFuZCB0aGVuIGl0IGlzIGluc3RhbGxlZCBmb3JldmVyLiAKaW5zdGFsbC5wYWNrYWdlcygibmNkZjQiKQpgYGAKCllvdSBzaG91bGQgc2VlIGEgbG9hZCBvZiBib3RoIGJsYWNrIGFuZCByZWQgdGV4dCBmbGFzaCBieSBvbiB0aGUgc2NyZWVuLiBUaGUgaW1wb3J0YW50IGJpdCBpcyB0aGUgbGFzdCBmZXcgbGluZXMsIHdoaWNoIHNob3VsZCBzYXkgdGhpbmcgaGF2ZSBiZWVuIGxvYWRlZCBvay4gU3BlY2lmaWNhbGx5LCB0aGUgZmluYWwgbGluZSBzaG91bGQgc2F5ICIqIERPTkUgKFt0aGUgcGFja2FnZSBuYW1lXSkiLiBUaGUgcGFja2FnZSBoYXMgbm93IGJlZW4gaW5zdGFsbGVkLCBidXQgaXQgc3RpbGwgbmVlZHMgdG8gYmUgbG9hZGVkIGludG8gUiAoYW5kIGxvYWRpbmcgdGhlIHBhY2thZ2UgbmVlZHMgdG8gYmUgZG9uZSBhdCB0aGUgc3RhcnQgb2YgZXZlcnkgc2NyaXB0IHdpdGggYSBwYWNrYWdlIGluIGl0KS4gTGV0J3MgZG8gdGhpcyBub3cuIAoKYGBge3J9CiMgTG9hZCB0aGUgIm5jZGY0IiBwYWNrYWdlIGludG8gUi4gVGhpcyBuZWVkcyB0byBiZSBkb25lIGF0IHRoZSBzdGFydCBvZiBldmVyeSBSIHNjcmlwdCB0aGF0IHVzZXMgdGhlIHBhY2thZ2UuIApsaWJyYXJ5KCJuY2RmNCIpCmBgYApZb3Ugc2hvdWxkbid0IHNlZSBhbnkgb3V0cHV0IGZyb20gcnVubmluZyB0aGlzIGxpbmUgb2YgY29kZSwgYnV0IHdlIGNhbiBub3cgdXNlIHRoZSBmdW5jdGlvbnMgaW4gdGhlIG5jZGY0IHBhY2thZ2UuIAoKV2hhdCB3ZSBhcmUgZ29pbmcgdG8gd2FsayB5b3UgdGhyb3VnaCBkb2luZyBpbiB0aGUgbmV4dCBmZXcgY29kZSBjaHVua3MgaXMgdGhlIHN0YW5kYXJkIHdvcmtmbG93IGZvciB1c2luZyBhIE5ldENERiBmaWxlIGluIFIuIFRoaXMgd29ya2Zsb3cgaXMgYXMgZm9sbG93czoKMS4gT3BlbiB0aGUgTmV0Q0RGIGZpbGUgY29ubmVjdGlvbiBpbiBSIHdpdGggdGhlICJuY19vcGVuKCkiIGZ1bmN0aW9uCjIuIEZpbmQgb3V0IHdoYXQgaXMgYWN0dWFsbHkgaW5zaWRlIHRoZSBuZXRjZGYgZmlsZSB3aXRoIHRoZSAicHJpbnQoKSIgZnVuY3Rpb24KMy4gRXh0cmFjdCB0aGUgdmFyaWFibGVzIHdlIHdhbnQgdG8gdXNlIGZyb20gdGhlIE5ldENERiBmaWxlIHVzaW5nIHRoZSAibmN2YXJfZ2V0KCkiIGZ1bmN0aW9uCjQuIEV4dHJhY3QgYW55IGF0dHJpYnV0ZXMgd2Ugd2FudCBmcm9tIHRoZSBOZXRDREYgZmlsZSB1c2luZyB0aGUgIm5jYXR0X2dldCgpIiBmdW5jdGlvbgo1LiBDbG9zZSB0aGUgTmV0Q0RGIGZpbGUgY29ubmVjdGlvbiBpbiBSIHdpdGggdGhlICJuY19jbG9zZSgpIiBmdW5jdGlvbi4KCkVhY2ggb2YgdGhlIHN0ZXBzIGFib3ZlIGlzIHF1aXRlIGVhc3ksIHNvIGRvbid0IHBhbmljISAKCkZpcnN0LCB3ZSB3YW50IHRvIG9wZW4gdGhlIE5ldENERiBmaWxlIGFuZCBsb29rIGF0IHRoZSByYW5nZXMgb2YgZGlmZmVyZW50IHZhcmlhYmxlcy4gTHVja2lseSB0aGlzIGlzIHF1aXRlIGVhc3kgaW4gUi4gRmlyc3Qgd2UgdXNlIHRoZSBuY2RmNCBsaWJyYXJ5IHRvIG9wZW4gYSBjb25uZWN0aW9uIHRvIHRoZSBmaWxlIGFuZCBzdG9yZSB0aGlzIGNvbm5lY3Rpb24gaW4gYSB2YXJpYWJsZSwgYW5kIHRoZW4gd2UgdXNlIHRoZSBwcmludCBmdW5jdGlvbiB0byBwcmludCBhIHN1bW1hcnkgb2YgdGhlIGZpbGUuIExldHMgZG8gdGhpcyBub3cuIFJlbWVtYmVyIGlmIHlvdSBoYXZuJ3QgcnVuIHRoZSBpbnN0YWxsLnBhY2thZ2VzKCkgYW5kIHRoZSBsaWJyYXJ5KCkgZnVuY3Rpb25zLCB0aGVuIFIgd2lsbCBjb21wbGFpbiB0aGF0IGl0ICJjYW4ndCBmaW5kIiB0aGUgZnVuY3Rpb24gLSB3aGljaCBtZWFucyBSIGRvZXNuJ3Qga25vdyB3aGF0IHRoZXNlIGZ1bmN0aW9ucyBhcmUgYmVjYXVzZSB0aGV5IGhhdm4ndCBiZWVuIGxvYWRlZCB5ZXQuIAoKYGBge3J9CiMgT3BlbiBhIGNvbm5lY3Rpb24gdG8gdGhlIE5ldENERiBmaWxlIGFuZCBzdG9yZSB0aGlzIGNvbm5lY3Rpb24gaW4gYSB2YXJpYWJsZSBjYWxsZWQgbmNmaWxlLiAoZG9uJ3Qgd29ycnkgYWJvdXQgd2hhdCB3ZSBtZWFuIGJ5IGEgImNvbm5lY3Rpb24iIHRvIHRoZSBmaWxlLCB0aGlzIHdpbGwgYmVjb21lIGNsZWFyIHRocm91Z2hvdXQgdGhlIGV4YW1wbGVzLikKbmNmaWxlIDwtIG5jX29wZW4oJzIwMTY3MjIxMzE1NTZFbnNlbWJsZUdQUF9NUl8xZGVnLm5jJykKIyBQcmludCB0aGUgaGVhZGVyIG9mIHRoZSBOZXRDREYgZmlsZSAoaS5lLiBwcmludCB0aGUgTmV0Q0RGIGZpbGUncyBtZXRhZGF0YSkKcHJpbnQobmNmaWxlKQpgYGAKCk91Y2gsIHRoYXQncyBxdWl0ZSBhIGxvdCBvZiB0ZXh0ISBUYWtlIGEgbW9tZW50IHRvIHJlYWQgaXQgYWxsLiBFdmVuIHRob3VnaCB0aGUgb3V0cHV0IGxvb2tzIGNvbmZ1c2luZywgdGhpcyBpcyB0aGUgc2FtZSBzb3J0IG9mIG91dHB1dCB5b3Ugd2lsbCBnZXQgZnJvbSBldmVyeSBOZXRDREYgZmlsZSB5b3Ugb3BlbiB3aXRoIFIsIHNvIGl0IHdpbGwgYmVjb21lIGVhc3kgYWZ0ZXIgeW91J3ZlIGRvbmUgaXQgYSBjb3VwbGUgb2YgdGltZXMuIAoKVGhlIGZpcnN0IGZldyBsaW5lcyBvZiB0aGUgb3V0cHV0IHNheToKPiAzIHZhcmlhYmxlcyAoZXhjbHVkaW5nIGRpbWVuc2lvbiB2YXJpYWJsZXMpOgpkb3VibGUgdGltZV9ibmRzW2JuZHMsdGltZV0KZmxvYXQgZ3BwW2xvbixsYXQsdGltZV0KICAoc29tZSBvdGhlciBvdXRwdXQpCmZsb2F0IHN0ZFtsb24sbGF0LHRpbWVdIAogIChzb21lIG90aGVyIG91dHB1dCkKClRoaXMgdGVsbHMgdXMgdGhlcmUgYXJlIDMgdmFyaWFibGVzIGluIHRoaXMgbmV0Y2RmIGZpbGUuIFRoZSBmaXJzdCB2YXJpYWJsZSBpcyB0aW1lX2JuZHMsIHRoZSBzZWNvbmQgaXMgZ3BwLCBhbmQgdGhlIHRoaXJkIGlzIHN0ZC4gWW91IHdpbGwgb2Z0ZW4gc2VlICJkb3VibGUiIGFuZCAiZmxvYXQiLiBUaGlzIHJlZmVycyB0byBob3cgbWFueSBkZWNpbWFsIHBsYWNlcyB0aGUgY29tcHV0ZXIgaXMgc3RvcmluZyBudW1iZXJzIGFzLiBUaGUgaW1wb3J0YW50IGJpdCBpcyB0aGF0IGl0IGlzIGp1c3QgYSBub3JtYWwgZGVjaW1hbCBudW1iZXIuICAKClRoZSBuZXh0IHBhcmFncmFwaCBvZiB0aGUgcHJpbnQoKSBvdXRwdXQgdGVsbHMgdXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHZhcmlhYmxlcyBpbiB0aGUgTmV0Q0RGIGZpbGU6Cgo+ICAgICA0IGRpbWVuc2lvbnM6CnRpbWUgIFNpemU6MzYwCiAgKHNvbWUgb3RoZXIgb3V0cHV0KQpibmRzICBTaXplOjIKICAoc29tZSBvdGhlciBvdXRwdXQpCmxvbiAgU2l6ZTozNjAKICAoc29tZSBvdGhlciBvdXRwdXQpCmxhdCAgU2l6ZToxODAKICAoc29tZSBvdGhlciBvdXRwdXQpCgpUaGlzIHRlbGxzIHVzIHRoYXQgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIGZpbGUgYXJlIGxhdHRpdHVkZSAoc3RvcmVkIGluIHRoZSB2YXJpYWJsZSBjYWxsZWQgImxhdCIpLCBsb25naXR1ZGUgKHN0b3JlZCBpbiB0aGUgdmFyaWFibGUgY2FsbGVkICJsb24iKSwgYW5kIHRpbWUgKHN0b3JlZCBpbiB0aGUgdmFyaWFibGUgY2FsbGVkICJ0aW1lIikuIFRoZXJlIGlzIGEgZm9ydGggdmFyaWFibGUgY2FsbGVkIGJuZHMgLSBkb24ndCB3b3JyeSBhYm91dCB0aGlzLCB3ZSBkb250IG5lZWQgaXQuIAoKQWZ0ZXIgdGhpcywgd2UgaGF2ZSBhIGxvbmcgcGFyYWdyYXBoIHdoaWNoIHN0YXJ0cyB3aXRoIAoKPiAxMyBnbG9iYWwgYXR0cmlidXRlczoKKGxvYWRzIG9mIG91dHB1dCkKCkluIGEgTmV0Q0RGIGZpbGUsIHRoZSBnbG9iYWwgYXR0cmlidXRlcyBhcmUgbm9ybWFsbHkgdXNlZnVsIHRoaW5ncyBsaWtlIHdobyBtYWRlIHRoZSBmaWxlLCB3aGVuIGl0IHdhcyBtYWRlLCBhIGRlc2NyaXB0aW9uLCBhbmQgb3RoZXIgdXNlZnVsIGluZm9ybWF0aW9uIHdoaWNoIGlzIG9mdGVuIGNhbGxlZCBtZXRhLWRhdGEuIAoKV2Ugbm93IHVuZGVyc3RhbmQgYSBiaXQgYWJvdXQgb3VyIE5ldENERiBmaWxlLiBUaGUgbmV4dCBzdGVwIGlzIHRvIGV4dHJhY3QgdmFyaWFibGVzIGluIHRoZSBOZXRDREYgZmlsZSBpbnRvIHZhcmlhYmxlcyBpbiBSLiBUbyBkbyB0aGlzIHdlIHVzZSB0aGUgIm5jdmFyX2dldCgpIiBmdW5jdGlvbi4gVGhlIGZ1bmN0aW9uIGFyZ3VtZW50cyBuYz0gcmVmZXJzIHRvIHRoZSBOZXRDZGYgZmlsZSwgd2hpbGUgdGhlIHZhcmlkPSBmdW5jdGlvbiBhcmd1bWVudCByZWZlcnMgdG8gdGhlIHZhcmlhYmxlIG5hbWUgKmluIHRoZSBOZXRDREYqIGZpbGUuIFRoaXMgaXMgd2h5IHdlIG5lZWRlZCB0byB1c2UgdGhlIHByaW50KCkgZnVuY3Rpb24gaW4gdGhlIGxhc3Qgc3RlcDsgdG8gZmluZCBvdXQgdGhlIHZhcmlhYmxlIG5hbWVzLiAKCmBgYHtyfQojIEV4dHJhY3QgdGhlICdsYXQnIHZhcmlhYmxlIGluIHRoZSBuZXRjZGYgZmlsZSwgYW5kIHN0b3JlIGl0IGluIGEgdmFyaWFibGUgY2FsbGVkICdsYXQnIGluIFIuCmxhdD1uY3Zhcl9nZXQobmM9bmNmaWxlLCB2YXJpZD0nbGF0JykKIyBFeHRyYWN0IHRoZSAnbG9uJyB2YXJpYWJsZSBpbiB0aGUgbmV0Y2RmIGZpbGUsIGFuZCBzdG9yZSBpdCBpbiBhIHZhcmlhYmxlIGNhbGxlZCAnbG9uJyBpbiBSLgpsb249bmN2YXJfZ2V0KG5jPW5jZmlsZSx2YXJpZD0nbG9uJykKIyBFeHRyYWN0IHRoZSAndGltZScgdmFyaWFibGUgaW4gdGhlIG5ldGNkZiBmaWxlLCBhbmQgc3RvcmUgaXQgaW4gYSB2YXJpYWJsZSBjYWxsZWQgJ3RpbWUnIGluIFIuCnRpbWU9bmN2YXJfZ2V0KG5jPW5jZmlsZSwgdmFyaWQ9InRpbWUiKQojIEV4dHJhY3QgdGhlICdncHAnIHZhcmlhYmxlIGluIHRoZSBuZXRjZGYgZmlsZSwgYW5kIHN0b3JlIGl0IGluIGEgdmFyaWFibGUgY2FsbGVkICdncHAnIGluIFIuCmdwcCA9IG5jdmFyX2dldChuYz1uY2ZpbGUsIHZhcmlkPSdncHAnKQpgYGAKCldlIG5vdyBoYXZlIDQgdmFyaWFibGVzIGluIFIgaG9sZGluZyB0aGUgaW5mb3JtYXRpb24gd2UgZXh0cmFjdGVkIGZyb20gdGhlIHZhcmlhYmxlcyBpbnNpZGUgdGhlIE5ldENERiBmaWxlLiBXZSB3YW50IHRvIGtub3cgd2hhdCBpcyBpbnNpZGUgdGhlc2UgdmFyaWFibGVzLiBXZSBjb3VsZCBqdXN0IHByaW50IHRoZWlyIHdob2xlIGNvbnRlbnRzIHRvIHRoZSBzY3JlZW4gYnkganVzdCBydW5uaW5nIHRoZWlyIG5hbWUgaW4gdGhlIHVzdWFsIHdheS4gSG93ZXZlciB3ZSBrbm93IHRoYXQgc29tZSBvZiB0aGVzZSB2YXJpYWJsZXMgbWlnaHQgYmUgdmVyeSwgdmVyeSBiaWcsIGFuZCB0aGlzIG1pZ2h0IGNhdXNlIHRoZSBjb21wdXRlciB0byBjcmFzaC4gQSBzYWZlciB3YXkgaXMgdG8gdXNlIHRoZSAibGVuZ3RoKCkiIGZ1bmN0aW9uIG9yIHRoZSAiZGltKCkiIGZ1bmN0aW9uIHRvIHNlZSBob3cgbG9uZyB0aGUgdmFyaWFibGUgaXMuIExldHMgZG8gdGhpczoKCmBgYHtyfQojIFVzZSB0aGUgbGVuZ3RoIGZ1bmN0aW9uIHRvIHNlZSBob3cgbG9uZyBlYWNoIHZhcmlhYmxlIGlzCmxlbmd0aChsYXQpCmxlbmd0aChsb24pCmxlbmd0aCh0aW1lKQpsZW5ndGgoZ3BwKQpgYGAKCk9rLCBzbyB0aGF0IGZpbmFsIHZhcmlhYmxlIGdwcCBoYXMgMjMgbWlsbGlvbiBlbnRyaWVzLiBQcm9iYWJseSBhIGdvb2QgdGhpbmcgd2UgZGlkbid0IGp1c3QgcHJpbnQgdGhhdCB0byB0aGUgc2NyZWVuLiAoQWxzbywgaWYgeW91J3JlIHN0aWxsIHRoaW5raW5nIHdoeSBhcmUgd2UgdXNpbmcgUiBub3QgZXhjZWwsIHRyeSBvcGVuaW5nIGEgMjMgbWlsbGlvbiBsaW5lIGxvbmcgZmlsZSBpbiBleGNlbCkuIFIgaGFzIHR3byB1c2VmdWwgZnVuY3Rpb25zLCAiaGVhZCgpIiBhbmQgInRhaWwoKSIsIGZvciBsb29raW5nIGF0IHRoZSBmaXJzdCBmZXcgb3IgbGFzdCBmZXcgZW50aXJlcyBvZiBhIHJlYWxseSBsb25nIGZpbGUgd2l0aG91dCBwcmludGluZyBldmVyeXRoaW5nLiBMZXRzIGhhdmUgYSBxdWljayBsb29rIGF0IHRoZSBmaXJzdCBmZXcgdmFsdWVzIG9mIGVhY2ggdmFyaWFibGUuIAoKYGBge3J9CiMgVXNlIHRoZSBoZWFkKCkgZnVuY3Rpb24gdG8gc2VlIHRoZSBmaXJzdCA1IGVudHJpZXMgb2YgZWFjaCB2YXJpYWJsZSwgdG8gaGVscCB1cyB1bmRlcnN0YW5kIG91dCBkYXRhLiAKaGVhZChsYXQpCmhlYWQobG9uKQpoZWFkKHRpbWUpCmhlYWQoZ3BwKQpgYGAKClNvIGl0IGxvb2tzIGxpa2UgdGhlIGxhdGl0dWRlIHZhcmlhYmxlIGdvZXMgZnJvbSAtOTAgdXB3YXJkcywgaS5lLiBmcm9tIHRoZSBzb3V0aCBwb2xlLiBUaGF0J3MgZmluZS4gVGhlIGxvbmdpdHVkZSB2YXJpYWJsZSBzZWVtcyB0byBnbyBmcm9tIC0xODAgdXB3YXJkcywgd2hpY2ggc291bmRzIGFib3V0IHJpZ2h0IHRvby4gVGhlIHRpbWUgdmFyaWFibGUgc2VlbXMgdG8gYmUgYSBiaXQgY3JhenkgdGhvdWdoISBXZSBzaG91bGQgbG9vayBiYWNrIGF0IHRoZSBtZXRhIGRhdGEgZnJvbSB0aGUgcHJpbnQoKSBmdW5jdGlvbi4gSWYgeW91IGxvb2sgYmFjaywgd2UgZmluZCBvdXQgdGhhdCB0aGUgdGltZSB2YXJpYWJsZSBpcyBpbiB1bml0cyBvZiAiZGF5cyBzaW5jZSAxNTgyLTEwLTE0IDAwOjAwOjAwIi4gT2ssIHNvIHdlIGFyZSBwcm9iYWJseSBsb29raW5nIGZvciBhIGJpZyBudW1iZXIgdGhlbi4gVGhlIGxhc3QgdmFyaWFibGUgZ3BwIHNlZW1zIHRvIGhhdmUgbG90cyBvZiBOQSdzIChzdGFuZGluZyBmb3Igbm90IGFwcGxpY2FibGUpLiBUaGlzIG1pZ2h0IG1lYW4gd2UgaGF2ZSBsb2FkZWQgaXQgaW5jb3JyZWN0bHksIG9yIGl0IG1pZ2h0IGp1c3QgYmUgYmVjYXVzZSB0aGVyZSBpcyBubyBkYXRhIGF0IHRoYXQgcG9pbnQuIExldHMgbG9vayBhdCB0aGUgZW5kIG9mIHRoZSB2YXJpYWJsZToKCmBgYHtyfQojIFByaW50IHRoZSBsYXN0IGZldyB2YWx1ZXMgaW4gdGhlIGdwcCB2YXJpYWJsZSB0byBzZWUgaWYgaXQgaXMgc3RpbGwgZnVsbCBvZiBOQXMuIAp0YWlsKGdwcCkKYGBgCgpIbW0uIFRoZSBlbmQgb2YgdGhlIHZhcmlhYmxlIGFsc28gc2VlbXMgdG8gYmUgZmlsbGVkIHdpdGggTkFzLiBXZSBtaWdodCBub3cgYmUgc2xpZ2h0bHkgd29ycmllZCBhYm91dCB3aGV0aGVyIGFsbCBvZiB0aGUgZ3BwIHZhcmlhYmxlIGlzIGZ1bGwgb2YgTkFzLiBBIHF1aWNrIHdheSB0byBjaGVjayB0aGlzIHdpdGhvdXQgcmVhZGluZyBhbGwgMjMgbWlsbGlvbiB2YWx1ZXMgaXMgdG8gc3VtIHVwIHRoZSB3aG9sZSB2ZWN0b3IgdXNpbmcgdGhlICJzdW0oKSIgZnVuY3Rpb24gYW5kIHNlZSBpZiBpdCBjb21lcyB0byBhIHZhbHVlIGdyZWF0ZXIgdGhhbiAwLiAqKk5vdGUqKiB5b3UgKm5lZWQqIHRoZSBuYS5ybT1UUlVFIGZ1bmN0aW9uIGFyZ3VtZW50LCB3aGljaCB0ZWxscyBSIHRvIHJlbW92ZSB0aGUgTkFzIGJlZm9yZSBzdW1taW5nLCBvdGhlcndpc2UgUiB3aWxsIHJldHVybiBOQS4gCgpgYGB7cn0KIyBSZW1vdmUgYWxsIHRoZSBOQXMsIGFuZCBzdW0gdXAgYW55IG51bWJlcnMgdGhhdCByZW1haW4uIElmIHdlIGdldCBhIHZhbHVlIG9mIDAsIHRoZXJlIGlzIG5vIGRhdGEgaW4gdGhlIGdwcCB2YXJpYWJsZSBhbmQgaXQgaXMgYWxsIE5Bcywgd2hpY2ggcHJvYmFibHkgbWVhbnMgd2UgaGF2ZSBsb2FkZWQgdGhlIGZpbGUgaW5jb3JyZWN0bHkuIElmIHdlIGdldCBhbnkgdmFsdWUgYWJvdmUgMCB0aGVuIHRoZSBncHAgdmFyaWFibGUgZG9lcyBoYXZlIHNvbWUgZGF0YSBpbiBpdC4gSWYgdGhlIGZ1bmN0aW9uIHJldHVybnMgTkEsIHRoZW4gd2UgaGF2ZSBmb3Jnb3R0ZW4gdG8gdXNlIHRoZSBuYS5ybT1UUlVFIGZ1bmN0aW9uIGFyZ3VlbW50LiAKc3VtKGdwcCwgbmEucm09VFJVRSkKYGBgCgpPaywgdGhlIHZhbHVlIGlzIGFib3ZlIDEuIFNvIHdlIGRvIGhhdmUgZGF0YSBpbiB0aGUgZ3BwIHZhcmlhYmxlLiBJdCdzIG9rLiAKClRoZSBsYXN0IHRoaW5nIHdlIHdhbnQgdG8gZG8gaXMgZXh0cmFjdCBzb21lIG9mIHRoZSBhdHRyaWJ1dGVzIGluIHRoZSBtZXRhLWRhdGEuIExldHMgZ2V0IHRoZSBmdWxsIG5hbWUgb2YgdGhlIGdwcCB2YXJpYWJsZSBhbmQgaXQncyB1bml0cy4gV2UgY2FuIHRoZW4gdXNlIHRoZXNlIGZvciB0aGluZ3MgbGlrZSBwbG90IHRpdGxlcyBsYXRlci4gTGV0J3MgYWxzbyBnZXQgdGhlIF9GaWxsVmFsdWUuIFRoaXMgaXMgdGhlIHZhbHVlIGluIHRoZSBOZXRDREYgZmlsZSB0aGF0IGNvcnJlc3BvbmRzIHRvIE5BLiBUaGUgZmFjdCB0aGF0IHdlIHNhdyBsb3RzIG9mIE5BcyB3aXRoIHRoZSBoZWFkIGFuZCB0YWlsIGZ1bmN0aW9uIG1pZ2h0IHNob3cgdGhhdCBSIGhhcyBtYW5hZ2VkIHRvIGhhbmRsZSBpdCBmb3IgdXMgYWxyZWFkeSwgYnV0IGl0J3MgYmV0dGVyIHRvIGJlIHNhZmUgdGhhbiBzb3JyeSEgCgpgYGB7cn0KIyBHZXQgdGhlIGxvbmcgbmFtZSBhdHRyaWJ1dGUgZm9yIHRoZSBncHAgdmFyaWFibGUsIGZvciBwbG90dGluZyBsYXRlcgpncHBfbmFtZT1uY2F0dF9nZXQobmNmaWxlLCAiZ3BwIiwgImxvbmdfbmFtZSIpCnByaW50KGdwcF9uYW1lKQojIEdldCB0aGUgdW5pdHMgb2YgdGhlIGdwcCB2YXJpYWJsZSwgZm9yIHBsb3R0aW5nIGxhdGVyIApncHBfdW5pdHM9bmNhdHRfZ2V0KG5jZmlsZSwgImdwcCIsICJ1bml0cyIpCnByaW50KGdwcF91bml0cykKIyBHZXQgdGhlIHZhbHVlIHRoYXQgY29ycmVzcG9uZHMgdG8gTkEgaW4gdGhlIGdwcCB2YXJpYWJsZS4gCmdwcF9maWxsdmFsdWUgPC0gbmNhdHRfZ2V0KG5jZmlsZSwiZ3BwIiwiX0ZpbGxWYWx1ZSIpCnByaW50KGdwcF9maWxsdmFsdWUpCmBgYAoKRnJvbSBwcmludGluZyB0aGVzZSwgd2UgY2FuIHNlZSB0aGF0IGVhY2ggZXh0cmFjdGVkIGF0dHJpYnV0ZSBoYXMgYW4gImhhc2F0dCIgYW5kIGEgInZhbHVlIi4gaGFzYXR0IHN0YW5kcyBmb3IgImhhcyBhdHRyaWJ1dGUiIGFuZCBpcyBlaXRoZXIgdHJ1ZSBvciBmYWxzZS4gSWYgaXQgaXMgdHJ1ZSwgdGhlICJ2YWx1ZSIgaG9sZHMgdGhlIHZhbHVlLiAKCldlIGhhdmUgbm93IGV4dHJhY3RlZCBhbGwgdGhlIGRhdGEgd2UgbmVlZCBmcm9tIHRoZSBOZXRDREYgZmlsZSBjb25uZWN0aW9uLiBXZSBjYW4gbm93IGNsb3NlIHRoZSBjb25uZWN0aW9uIHRvIHRoZSBmaWxlLiBUaGlzIGZyZWVzIHVwIHNvbWUgY29tcHV0ZXIgbWVtb3J5IGZvciB1cy4gTm90ZSB0aGF0IHdlIGRpZG4ndCBuZWVkIHRvIGRvIHRoaXMgaW4gdGhlIGxhc3Qgbm90ZWJvb2sgZm9yIHRoZSAicmVhZC5jc3YoKSIgYW5kICJyZWFkLnRhYmxlKCkiIGZ1bmN0aW9ucywgd2hpY2ggY2xvc2UgdGhlIGRhdGEgZmlsZSBhdXRvbWF0aWNhbGx5LiAKCmBgYHtyfQpuY19jbG9zZShuY2ZpbGUpCmBgYAoKTm93IGZvciBhIHN1cGVyIHVzZWZ1bCBsaXR0bGUgbGluZSBvZiBjb2RlLiBOZXRDREYgZmlsZXMgb2Z0ZW4gaGF2ZSBOQSB2YWx1ZXMgdGhhdCBjb3JyZXNwb25kIHRvIGEgdmVyeSBiaWcgb3IgdmVyeSBzYW1sbCBudW1iZXIuIEZvciBleGFtcGxlLCBhIGNvbW1vbiBudW1iZXIgdG8gcmVwcmVzZW50IE5BIGlzIC05OTk5Ljk5LiBJZiB3ZSB0cnkgdG8gcGxvdCB0aGUgZ3BwIHZhcmlhYmxlIGluIFIsIHRoZSBjb2xvdXIgc2NhbGUgd2lsbCBleHBhbmQgdG8gLTk5OTkuOTkgYW5kIHNvIHRoZSBtYXAgd2lsbCBiZSBpbXBvc3NpYmxlIHRvIHVuZGVyc3RhbmQuIEluc3RlYWQgd2Ugd2FudCB0byBzZXQgdGhlc2UgLTk5OTkuOTkgdmFsdWVzIChvciB3aGF0ZXZlciB0aGUgTmV0Q0RGJ3MgX0ZpbGxWYWx1ZSBpcykgdG8gTkEgaW5zaWRlIFIuIFIgd2lsbCB0aGVuIG5vdCBwbG90IGFueXRoaW5nIHdoaWNoIGlzIE5BLCBhbmQgb3VyIHBsb3RzIHdpbGwgYmUgaHVtYW4gcmVhZGFibGUuIAoKVGhlIGxpbmUgb2YgY29kZSBiZWxvdyBpcyBzaG9ydCwgYnV0IHF1aXRlIGNvbXBsZXguIFdoYXQgdGhlIGxpbmUgb2YgY29kZSBkb2VzLCBpcyB1c2UgdGhlIHNxdWFyZSBicmFja2V0cyBub3RhdGlvbiB0byBleHRyYWN0IGFueSB2YWx1ZXMgaW4gdGhlIGdwcCB2YXJpYWJsZSB3aGljaCBlcXVhbCB0aGUgdmFsdWUgaW4gZ3BwX2ZpbGx2YWx1ZXMsIGFuZCBzZXQgdGhvc2UgZXh0cmFjdGVkIHZhbHVlcyB0byBOQS4gSWYgeW91IG5lZWQgdG8gdXNlIHRoaXMgbGluZSBvZiBjb2RlIGluIHlvdXIgb3duIHdvcmssIGp1c3QgY29weSBhbmQgcGFzdGUgYW5kIGNoYW5nZSB0aGUgdmFyaWFibGUgbmFtZXMgdG8gdGhlIHZhcmlhYmxlIG5hbWVzIGluIHlvdXIgd29yay4gCgpgYGB7cn0KIyByZXBsYWNlIG5ldENERiBmaWxsIHZhbHVlcyB3aXRoIE5BJ3MKZ3BwW2dwcD09Z3BwX2ZpbGx2YWx1ZSR2YWx1ZV0gPSBOQQpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgZXh0cmFjdGVkIGFsbCB0aGUgZGF0YSBpbiBvdXIgTmV0Q0RGIGZpbGUsIHdlIGFyZSByZWFkeSB0byBzdGFydCBtYWtpbmcgc29tZSBtYXBzIG9mIHRoZSBkYXRhISAKCiMjIE1ha2luZyBvdXIgZmlyc3QgbWFwIApUaGUgZGF0YSBpbnNpZGUgYSBOZXRDREYgZmlsZSAoYW5kIHRoZXJlZm9yZSB0aGUgZGF0YSBpbiBvdXIgdmFyaWFibGVzIGluIFIgd2hpY2ggd2UgaGF2ZSBleHRyYWN0ZWQgZnJvbSB0aGUgTmV0Q0RGIGZpbGVzKSBpcyBhIHJlY3Rhbmd1bGFyIGdyaWQgb2YgbnVtYmVycy4gSW4gZmFjdCwgaWYgd2UgaGF2ZSBhIHRpbWUgdmFyaWFibGUsIHRoZW4gaXQgaXMgYSAzRCBjdWJlIG9mIGRhdGEgLSBpbWFnaW5lIGEgcnViaXggY3ViZSBvZiBkYXRhLCBidXQgd2l0aCB0aGUgZGltZW5zaW9ucyBvZiBvdXIgZGF0YSBpbnN0ZWFkIG9mIHRoZSBub3JtYWwgM3gzeDMgcnViaXggY3ViZS4gCgpSZW1lbWJlciB3ZSBleHRyYWN0ZWQgZm91ciBkYXRhIHZhcmlhYmxlcyBmcm9tIHRoZSBOZXRDREYgZmlsZSB3aXRoIHRoZSBuY3Zhcl9nZXQoKSBmdW5jdGlvbi4gVGhvc2UgdmFyaWFibGVzIHdlcmUgbGF0LCBsb24sIHRpbWUsIGFuZCBncHAuIAoKTGV0J3MgcmVtaW5kIG91cnNlbHZlcyBvZiB0aGUgZGltZW5zaW9ucyBvZiBvdXIgZGF0YToKCmBgYHtyfQpkaW0oZ3BwKQpgYGAKClNvIDM2MCBpbiB0aGUgeCBkaW1lbnNpb24sIDE4MCBpbiB0aGUgeSBkaW1lbnNpb24sIGFuZCAzNjAgaW4gdGhlIHogZGltZW5zaW9uLiBJZiB3ZSBjaGVjayBiYWNrIHRvIHRoZSBsZW5ndGggb2YgdGhlIHZhcmlhYmxlcyBmcm9tIHRoZSBwcmludCgpIGZ1bmN0aW9uIHdlIGNhbiBzZWUgdGhpcyBjb3JyZXNwb25kcyB0byBsYXQsIGxvbmcgYW5kIHRpbWUuIAoKVGhpcyBpcyBqdXN0IGEgbWF0cml4IG9mIGRhdGEsIHNvIHdlIGNhbiBleHRyYWN0IGFueSBzdWJzZXQgb2YgaXQgdXNpbmcgdGhlIHNxdWFyZSBicmFja2V0IG5vdGF0aW9uLiBMZXQncyBleHRyYWN0IHRoZSBmaXJzdCB0aW1lIHNsaWNlIG9mIHRoZSBkYXRhLCBhbmQgc2F2ZSBpdCBpbiBhIHZhcmlhYmxlIGNhbGxlZCBmaXJzdF9ncHBfc2xpY2UuIAoKYGBge3J9CmZpcnN0X2dwcF9zbGljZSA9IGdwcFssLDFdCmBgYAoKUmVtZW1iZXIgdXNpbmcgdGhlIHNxdWFyZSBicmFja2V0IG5vdGF0aW9uIHRvIHNsaWNlIGRhdGEgaW4gdGhlIGxhc3QgUiBub3RlYm9vaz8gSW4gdGhhdCBub3RlYm9vaywgdXNlIHNhaWQgaG93IGZvciAyRCBkYXRhIHRoZSBmb3JtYXQgaXMgc29tZV92YXJpYWJsZVt4X2RpbWVuc2lvbix5X2RpbWVuc2lvbl0uIFRoaXMgbWVhbnMgdG8gc2VsZWN0IHRoZSBmaXJzdCByb3cgb2YgdGhlIGZpcnN0IGNvbHVtbiB3b3VsZCBiZSBzb21lX3ZhcmlhYmxlWzEsMV0uIEluIHRoYXQgbm90ZWJvb2sgd2UgYWxzbyBtZW50aW9uZWQgdGhhdCBpZiB3ZSBsZWF2ZSB0aGUgcGxhY2VzIGJlZm9yZSBvciBhZnRlciB0aGUgY29tbWEgaW5zaWRlIHRoZSBzcXVhcmUgYnJhY2tldHMgYmxhbmsgKGxpa2UgdGhpczogc29tZV92YXJpYWJsZVssMV0gb3Igc29tZV92YXJpYWJsZVsxLF0pIHRoYXQgd2Ugc2VsZWN0IGFsbCBvZiB0aGF0IGNvbHVtbiBvciBhbGwgb2YgdGhhdCByb3cuIAoKSG9wZWZ1bGx5IHRoYXQgaXMgbm90IHRvbyBjb25mdXNpbmcgc28gZmFyLiBTbyB3ZSBrbm93IG91ciBncHAgdmFyaWFibGUgY29udGFpbnMgbGF0dGl0dWRlIHggbG9uZ2l0dWRlIHggdGltZS4gVGhlcmVmb3JlIHJ1bm5pbmcgZ3BwWywsMV0gbWVhbnMgZ2l2ZSBtZSBhbGwgbGF0dGl0dWRlIGFuZCBhbGwgbG9uZ2l0dWRlIGZvciB0aGUgZmlyc3QgdGltZSBzbGljZSBvbmx5LiAKCktub3dpbmcgYWxsIG9mIHRoaXMsIGlmIHdlIHJ1biBvdXIgdHJ1c3R5IGRpbSgpIGZ1bmN0aW9uIG9uIG91ciBuZXcgdmFyaWFibGUgZmlyc3RfZ3BwX3NsaWNlLCB3ZSBzaG91bGQgZ2V0IHJlc3VsdHMgb2YgMzYwIHggMTgwIHggMSwgYXMgd2UgdG9vayBhbGwgbG9uZ3R1ZGUgYW5kIGFsbCBsYXR0aXR1ZGUsIGF0IHRoZSBmaXJzdCB0aW1lIG9ubHkgKG5vdCBhdCBhbGwgdGltZSBzbGljZXMpLiBTbyBlc3NlbnRpYWxseSB3ZSB0b29rIHRoZSBmaXJzdCBsYXllciBvZmYgb3VyIHJ1Yml4IGN1YmUuIAoKTGV0cyBjaGVjayB3ZSBnZXQgMzYwIHggMTgwIHggMSB3aXRoIG91ciBkaW0oKSBmdW5jdGlvbi4gCgpgYGB7cn0KZGltKGZpcnN0X2dwcF9zbGljZSkKYGBgCgpPaywgc28gd2UgZ2V0IDM2MCB4IDE4MCwgd2hpY2ggaXMgdGhlIHNhbWUgYXMgMzYwIHggMTgwIHggMS4gU28gd2Ugbm93IGhhdmUgYSBzaW5nbGUgdGltZSBzbGljZSBzdG9yZWQgaW4gYSBtYXRyaXguIFdlIGNhbiBlYXNpbHkgbWFrZSBhIHBpY3R1cmUgZnJvbSBhIG1hdHJpeCB1c2luZyB0aGUgaW1hZ2UgZnVuY3Rpb246CgpgYGB7cn0KaW1hZ2UoZmlyc3RfZ3BwX3NsaWNlKQpgYGAKCkNvb2whIEJ1dCB0aGVyZSBpcyBhbiBvYnZpb3VzIHByb2JsZW0gLSB0aGUgd29ybGQgc2VlbXMgdG8gYmUgdXBzaWRlIGRvd24uIEx1Y2tpbHkgdGhpcyBpcyBlYXNpbHkgZml4ZWQuIFdlIGp1c3Qgc2xpZ2h0bHkgbW9kaWZ5IGhvdyB3ZSBhcmUgc2xpY2luZyB0aGUgZ3BwIHZhcmlhYmxlLiBZb3Ugc2VlLCB3aGVuIHdlIHdyaXRlIGdwcFssLDFdIHRvIHJlcHJlc2VudCAiZ2l2ZSBtZSBhbGwgdGhlIGxhdHRpdHVkZSBhbmQgYWxsIHRoZSBsb25naXR1ZGUgdmFsdWVzIGZvciB0aGUgZmlyc3QgdGltZSBzbGljZSIsIHVuZGVyIHRoZSBob29kIFIgaXMgdHJlYXRpbmcgdGhpcyB0aGUgc2FtZSBhcyBpZiB3ZSBoYWQgd3JpdHRlbiBncHBbIDE6MzYwICwgMToxODAgLCAxIF0uIFRoZXJlZm9yZSB0byBmbGlwIHRoZSBtYXRyaXggaW4gdGhlIGxhdHRpdHVkZSBkaXJlY3Rpb24sIHdlIGp1c3QgY2hhbmdlIHRoZSAxOjE4MCB0byAxODA6MSB0byByZXZlcnNlIGl0LiBTZWUgYmVsb3c6IAoKYGBge3J9CmZsaXBwZWRfZmlyc3RfZ3BwX3NsaWNlID0gZ3BwWywxODA6MSwxXQppbWFnZShmbGlwcGVkX2ZpcnN0X2dwcF9zbGljZSkKYGBgCgpPaywgbm93IHdlJ3JlIGNvb2tpbmchIFRoaXMgaXMgYSBtYXAgb2YgdGhlIGZpcnN0IHRpbWUgc2xpY2Ugb2Ygb3VyIGdwcCB2YXJpYWJsZSEgQnV0IGZvciBpdCB0byBiZSBhIGRlY2VudCBtYXAsIHdlIG5lZWQgYSBjb3VwbGUgb2YgdGhpbmdzLiBGaXJzdCwgaXQgd291bGQgYmUgYmVzdCB0byBhZGQgYSBzY2FsZS4gSXQgbG9va3MgbGlrZSB0aGUgYXhlcyBhcmUgY3VycmVudGx5IGp1c3QgZ29pbmcgZnJvbSAwIHRvIDEgYXMgd2VsbCAtIHdlIHByb2JhYmx5IHdhbnQgdG8gY2hhbmdlIHRoYXQuIEl0IG1pZ2h0IGJlIHVzZWZ1bCB0byBiZSBhYmxlIHRvIGNoYW5nZSBjb2xvdXIgc2NhbGVzIHRvby4gQW5kIG1heWJlIGEgdGl0bGUgd291bGQgYmUgYSBnb29kIGlkZWEgdG9vLiBMZXQncyBzZXQgYWJvdXQgbW9kaWZ5aW5nIG91ciBwbG90LiAKCiMjIEFkZGluZyBhIGxlZ2VuZCAKU28gaXQgdHVybnMgb3V0IHdlIGhhdmUgYW4gaW1tZWRpYXRlIHByb2JsZW0uIEFkZGluZyBhIGxlZ2VuZCB0byB0aGUgaW1hZ2UoKSBmdW5jdGlvbiBpcyByZWFsbHkgaGFyZC4gRG9oISBUaGUgaW1hZ2UgZnVuY3Rpb24gaXMgc3RpbGwgcmVhbGx5IHVzZWZ1bCBhcyBhIHF1aWNrIGZpcnN0IGxvb2sgYXQgYSBtYXRyaXggb2YgbnVtYmVycyB0aG91Z2guIEhvd2V2ZXIgaXQganVzdCBzbyBoYXBwZW5zIHRoYXQgaW4gdGhlIGZpZWxkcyBwYWNrYWdlLCBzb21lIGtpbmQgUiBwcm9ncmFtbWVyIGhhcyBtb2RpZmllZCB0aGUgaW1hZ2UoKSBmdW5jdGlvbiBpbnRvIGEgbmV3IGZ1bmN0aW9uIGNhbGxlZCBpbWFnZS5wbG90KCksIHdoaWNoIGhhcyBhbiBhdXRvbWF0aWMgY29sb3VyIGJhci4gSXQgYWxzbyBhbGxvd3MgdXMgdG8gc3BlY2lmeSB0aGUgbGFiZWxzIG9mIHRoZSB4IGFuZCB5IGF4aXMsIHNvIHdlIGNhbiBhY3R1YWxseSBkaXNwbGF5IGxhdHRpdHVkZSBhbmQgbG9uZ2l0dWRlIHJhdGhlciB0aGFuIHRoZSBzdHJhbmdlIDAgdG8gMSBzY2FsZS4gVW5jb21tZW50IHRoZSBpbnN0YWxsLnBhY2thZ2VzIGxpbmUgdW5kZXJuZWF0aCB0byBpbnN0YWxsIHRoZSBmaWVsZHMgcGFja2FnZSwgdGhlbiBydW4gdGhlIGxpYnJhcnkgY29tbWFuZCB0byBsb2FkIHRoZSBwYWNrYWdlIGFuZCB0aGUgbWFrZSB0aGUgbWFwOiAKCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoImZpZWxkcyIpCmxpYnJhcnkoImZpZWxkcyIpCmltYWdlLnBsb3QobG9uLCBsYXQsIGZsaXBwZWRfZmlyc3RfZ3BwX3NsaWNlKQpgYGAKCkNvb2whIExvb2tpbmcgYmV0dGVyIGFscmVhZHkuIFRoZXJlIGlzIGEgdXNlZnVsIGZ1bmN0aW9uIGNhbGxlZCBtYXAoKSBpbiBSIHdoaWNoIGFsbG93cyB1cyB0byBhZGQgY291bnRyeSBvdXRsaW5lcy4gVGhpcyBpcyBvZnRlbiBhIGdvb2QgaWRlYSB0byBhZGQsIGFzIGl0IHNob3dzIHRoZXJlIGlzIG5vIGRhdGEgYXQgYWxsIGZvciBBbnRhcmN0aWNhLCBHcmVlbmxhbmQsIGFuZCB0aGUgU2FoYXJhLiBOb3RlIHRoZSBhZGQ9VCAoc2hvcnQgZm9yIGFkZD1UUlVFKSwgd2hpY2ggYWRkcyB0byB0aGUgY3VycmVudCBwbG90LiBUcnkgY2hhbmdpbmcgc29tZSBvZiB0aGUgZnVuY3Rpb24gYXJndW1lbnRzIHRvIGN1c3RvbWlzZSB0aGUgcGxvdC4gCgpgYGB7cn0KaW1hZ2UucGxvdChsb24sIGxhdCwgZmxpcHBlZF9maXJzdF9ncHBfc2xpY2UpCm1hcChkYXRhYmFzZSA9ICd3b3JsZCcsIGx3ZD0xLjUsIGFkZCA9IFQsIGNvbD0nYmxhY2snKQpgYGAKCiMjIENoYW5naW5nIGNvbG91ciBwYWxsZXRzIApUaGVyZSBhcmUgYSBudW1iZXIgb2Ygc3RhbmRhcmQgY29sb3IgcGFsZXR0ZXMgaW4gUi4gVGhlIHJhaW5ib3cgY29sb3VyIHBhbGxldGUgaXMgc3RhbmRhcmQgaW4gaW1hZ2UucGxvdCgpLiBIb3dldmVyIGEgbnVtYmVyIG9mIHJlY2VudCBzY2llbnRpZmljIGFydGljbGVzIGhhdmUgcG9pbnRlZCBvdXQgdGhhdCByYWluYm93IGNvbG91ciBwYWxsZXRlcyBjYW4gZHJhdyB0aGUgZXllIHRvIHVuaW1wb3JhbnQgZmVhdHVyZXMgYXMgc29tZSBjb2xvciBjaGFuZ2VzIGFjdCBsaWtlIGNvbnRvdXJzIGRpZmZlcmVudGx5IHRvIGRpZmZlcmVudCBwZW9wbGUuIFRoZXJlZm9yZSBzaW1wbGVyIGNvbG91ciBwYWxsZXRzIGNhbiBvZnRlbiBiZSBiZXR0ZXIgZm9yIGRpc3BsYXlpbmcgc2NpZW50aWZpYyBkYXRhLiBMZXQncyBpbnN0YWxsIHRoZSBSQ29sb3JCcmV3ZXIgcGFja2FnZSB0byBnaXZlIHVzIGFjY2VzcyB0byBhIGxvYWQgb2YgdXNlZnVsIGNvbG9yIHBhbGxldGVzLiBUaGUgUkNvbG9yQnJld2VyIHBhY2thZ2VzIGdpdmVzIHVzIGFjY2VzcyB0byB0aGUgYnJld2VyLnBhbCgpIGZ1bmN0aW9uLiBUaGlzIGFsbG93cyB1cyB0byBnaXZlIGEgbnVtYmVyIG9mIGJyZWFrcyBpbiB0aGUgY29sb3VyIHBhbGxldGUgd2Ugd2FudCAobm9ybWFsbHkgdXAgdG8gOSBvciAxMCwgZGVwZW5kaW5nIG9uIHRoZSBwYWxsZXRlKSBhcyB0aGUgZmlyc3QgZnVuY3Rpb24gYXJndW1lbnQsIGFuZCB0aGUgbmFtZSBvZiB0aGUgY29sb3VyIHBhbGxldGUgYXMgdGhlIHNlY29uZCBhcmd1bWVudC4gU28gYnJld2VyLnBhbCgxMCwgIlJkQnUiKSBnaXZlcyB1cyBhIGNvbG9yIHBhbGxldGUgZnJvbSByZWQgdG8gYmx1ZSB3aXRoIDEwIGJyZWFrcyBpbiBpdC4gVGhpcyBpcyBjb29sLCBidXQgd2Uga25vdyBsb3cgbnVtYmVycyBmb3Igb3VyIGdwcCBkYXRhIGFyZSBub3JtYWxseSBkaXNwbGF5ZWQgd2l0aCBjb2xkIGNvbG91cnMgaW4gZW52aXJvbm1lbnRhbCBzY2llbmNlcywgc28gd2UgdXNlIHRoZSByZXYoKSBmdW5jdGlvbiB0byByZXZlcnNlIHRoZSBvcmRlciwgbWFraW5nIHVzIGEgYmx1ZSB0byByZWQgY29sb3VyIHBhbGxldGUuIFNlZSBiZWxvdzogCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoIlJDb2xvckJyZXdlciIpCmltYWdlLnBsb3QobG9uLCBsYXQsIGZsaXBwZWRfZmlyc3RfZ3BwX3NsaWNlLCBjb2wgPSByZXYoYnJld2VyLnBhbCgxMCwgIlJkQnUiKSkpCm1hcChkYXRhYmFzZSA9ICd3b3JsZCcsIGFkZCA9IFQsIGx3ZD0xLjUpCmBgYAoKWW91IGNhbiBmaW5kIG1vcmUgYWJvdXQgd2hhdCBjb2xvciBwYWxsZXRlcyBhcmUgZm91bmQgaW4gdGhlIFJDb2xvckJyZXdlciBvbiB0aGlzIGxpbmsgIVtdKGh0dHBzOi8vZWFybGdseW5uLmdpdGh1Yi5pby9STm90ZXMvcGFja2FnZS9SQ29sb3JCcmV3ZXIvaW5kZXguaHRtbCkuIExldCdzIHRyeSB0aGUgeWVsbG93LWdyZWVuLWJsdWUgcGFsbGV0ZSBpbnN0ZWFkLCBqdXN0IGZvciBwcmFjdGlzZTogCgpgYGB7cn0KaW1hZ2UucGxvdChsb24sIGxhdCwgZmxpcHBlZF9maXJzdF9ncHBfc2xpY2UsIGNvbCA9IHJldihicmV3ZXIucGFsKDksIllsR25CdSIpKSkKbWFwKGRhdGFiYXNlID0gJ3dvcmxkJywgYWRkID0gVCwgbHdkPTEuNSkKYGBgCgpPaywgbm93IGxldHMgbWFrZSB0aGUgcGxvdCBpdHNlbGYgYSBiaXQgYmV0dGVyLiBMZXRzIGFkZCBhIHRpdGxlLCBhbmQgcmVtb3ZlIHRoZSB4IGFuZCB5IGxhYmVscyB1c2luZyB0aGUgbWFpbj0sIHhsYWI9IGFuZCB5bGFiPSBmdW5jdGlvbiBhcmd1bWVudHMgd2UgaGF2ZSBhbHJlYWR5IGxlYXJudC4gV2UgY2FuIHVzZSBvdXIgZ3BwX25hbWUgdmFyaWFibGUgdGhhdCB3ZSBleHRyYWN0ZWQgZnJvbSB0aGUgTmV0Q0RGIGZpbGUgbmVhciB0aGUgdG9wIG9mIHRoaXMgc2NyaXB0LCBhbmQgYWNjZXNzIGl0J3MgY29udGVudHMgYnkgY2FsbGluZyBncHBfbmFtZSR2YWx1ZS4gSWYgYWxsIG9mIHRoaXMgaXMgc2VlbWluZyBsaWtlIGFuIG92ZXJ3aGVsbWluZyBhbW91bnQgdG8gcmVtZW1iZXIsIGJlYXIgaW4gbWluZCB5b3UgY2FuIGp1c3QgY29weSBhbmQgcGFzdGUgY29kZSBmcm9tIHRoaXMgZG9jdW1lbnQgYW5kIGp1c3QgbW9kaWZ5IG5hbWVzLiBZb3UgZG9uJ3QgaGF2ZSB0byByZW1lbWJlciBhbGwgb2YgdGhpcy4gVGhlcmUgaXMgbm8gZXhhbSBvbiBSIHN5bnRheCA6KSAKCklmIHlvdSBjaGFuZ2UganVzdCB0aGVzZSBhcmd1bWVudHMgYW5kIHJ1biB0aGUgY29kZSB5b3Ugd2lsbCBmaW5kIGhhbGYgdGhlIHRpdGxlIGhhcyBiZWVuIGNob3BwZWQgb2ZmIHRoZSBzaWRlIG9mIHRoZSBwbG90ISBBZ2dnaGhoISBUaGlzIGlzIGJlY2F1c2UgdGhlIHRpdGxlIGlzIHF1aXRlIGxvbmcsIGFuZCBSIGJ5IGRlZmF1bHQgaGFzIGEgZml4ZWQgYW1vdW50IG9mIHBhZGRpbmcgYXJvdW5kIHBsb3RzIHdoaWNoIG91ciBsb25nIHRpdGxlIHJ1bnMgb3Zlci4gVGhpcyBpcyByYXJlbHkgYSBwcm9ibGVtLCBidXQgdGhpcyBpcyBvbmUgb2YgdGhvc2UgZXhjZXB0aW9ucyEgV2UgY2FuIGZpeCB0aGlzIGJ5IGNhbGxpbmcgdGhlIHBhcigpIGZ1bmN0aW9uIChzaG9ydCBmb3IgcGxvdCBQQVJhbWV0ZXJzKSBhbmQgY2hhbmdlIHRoZSBtYXI9YygpIGFyZ3VtZW50IChzaG9ydCBmb3IgTUFSZ2lucykuIFRoZSBkZWZhdWx0IGZvciB0aGlzIGlzIHBhcihtYXI9Yyg1LjEsIDQuMSwgNC4xLCAyLjEpKS4gVGhpcyBpcyBmb3IgZ3JhcGhpYyBkZXNpZ24gcmVhc29ucyB0aGF0IEkgaW1hZ2luZSBzb21lb25lIHNwZW50IGFnZXMgZGVjaWRpbmcgb24uIFRoZSBudW1iZXJzIHJlZmVyIHRvIHRoZSBib3R0b20sIGxlZnQsIHRvcCBhbmQgcmlnaHQgaGFuZCBtYXJnaW5zIHJlc3BlY3RpdmVseS4gVGhlcmVmb3JlIGNoYW5naW5nIHRoZSBzZWNvbmQgb3IgZm91dGggbnVtYmVyIHRvIGEgc21hbGxlciB2YWx1ZSBzaG91bGQgZml4IG91ciBwcm9ibGVtLiAKCkZpbmFsbHksIHdlIHdhbnQgdG8gYWRkIHRoZSB1bml0cyBvZiBvdXIgcGxvdCB0byB0aGUgbGVnZW5kLiBUbyBkbyB0aGlzIEkgZ29vZ2xlZCB0aGUgaW1hZ2UucGxvdCBmdW5jdGlvbiBhbmQgZm91bmQgdGhpcyBwYWdlICFbXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZmllbGRzL3ZlcnNpb25zLzkuNi90b3BpY3MvaW1hZ2UucGxvdCkgd2hpY2ggdGVsbHMgdXMgdGhhdCB0aGUgbGVnZW5kLmxhYj0gYXJndW1lbnQgYWxsb3dzIHVzIHRvIHNwZWNpZnkgdGhlIHRleHQsIHRoZSBsZWdlbmQubGluZT0gYXJndW1lbnQgbW92ZXMgdGhlIGxlZ2VuZCB0ZXh0IHRvIHRoZSBzaWRlIHNvIGl0IGlzIG5vdCB1bmRlcm5lYXRoIHRoZSBsZWdlbmQgbGFiZWxzLCBhbmQgdGhlIGxlZ2VuZC5tYXI9IGV4cGFuZHMgdGhlIGxlZ2VuZCBtYXJnaW4uIFRyeSBjaGFuZ2luZyB0aGUgdmFsdWVzIGFuZCByZXJ1bm5pbmcgdGhlIGNvZGUgYmxvY2sgc2V2ZXJhbCB0aW1lcyB0byBnZXQgYSBmZWVsaW5nIGZvciB3aGF0IGVhY2ggYXJndW1lbnQgZG9lcy4gCgpgYGB7cn0KcGFyKG1hcj1jKDUuMSwzLDQuMSwzKSkKaW1hZ2UucGxvdChsb24sIGxhdCwgZmxpcHBlZF9maXJzdF9ncHBfc2xpY2UsIGNvbCA9IHJldihicmV3ZXIucGFsKDksIllsR25CdSIpKSwgeGxhYj0iIiwgeWxhYj0iIiwgbWFpbj1ncHBfbmFtZSR2YWx1ZSwgbGVnZW5kLmxhYj1ncHBfdW5pdHMkdmFsdWUsIGxlZ2VuZC5saW5lPTQsIGxlZ2VuZC5tYXI9NykKbWFwKGRhdGFiYXNlID0gJ3dvcmxkJywgYWRkID0gVCwgbHdkPTEuNSkKYGBgCgojIyBDb2xvdXJibGluZCBmcmllbmRseSAvIHByaW50aW5nIGluIGJsYWNrIGFuZCB3aGl0ZSBmcmllbmRseSBjb2xvcnM/IApBIGZpbmFsIHF1aWNrIG5vdGUgb24gYSBzZXQgb2YgY29sb3VyIHBhbGxldGVzIGZvdW5kIGluIFB5dGhvbiBhbmQgTWF0bGFiIGNhbGxlZCBWaXJpZGlzLCBJbmZlcm5vLCBNYWdtYSBhbmQgQ3ViaGVsaXgsIHdoaWNoIGhhdmUgYmVjb21lIHBvcHVsYXIgYXMgdGhleSBhcmUgY29sb3VyYmxpbmQgZnJpZW5kbHksIGJsYWNrIGFuZCB3aGl0ZSBwcmludGVyIGZyaWVuZGx5ICh0cnkgcHJpbnRpbmcgYSByYWluYm93IGNvbG91ciBwYWxsZXRlIGluIGJsYWNrIGFuZCB3aGl0ZSAtIHRoZXJlIGVuZHMgdXAgYmVpbmcgc2V2ZXJhbCBkYXJrIGJhbmRzLCBtYWtpbmcgdGhlIGNvbG91cm1hcHMgdW51c2FibGUgaW4gYmxhY2sgYW5kIHdoaXRlKSBhbmQgcGVyY2VwdHVhbCAoaS5lLiB0aGV5IGRvbid0IGhhdmUgdGhlIHJhbmRvbSBjb250b3VyaW5nIGVmZmVjdCBpbiBkaWZmZXJlbnQgcGVvcGxlcyB2aXNpb24gZW1waGFzaXppbmcgZGlmZmVyZW50IHRoaW5ncykuIFRoZXNlIGFyZSBhdmFsaWFibGUgaW4gdGhlIHZpcmlkaXMgcGFja2FnZS4gQ2hlY2sgb3V0IHRoaXMgbGluayBmb3IgbW9yZSBvbiBob3cgdG8gdXNlIHRoZSBwYWNrYWdlICFbXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdmlyaWRpcy92aWduZXR0ZXMvaW50cm8tdG8tdmlyaWRpcy5odG1sKSwgYW5kIHNlZSBiZWxvdyBmb3IgYW4gZXhhbXBsZSBvZiB1c2luZyBpdDogCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJ2aXJpZGlzIikKbGlicmFyeSgidmlyaWRpcyIpCnBhcihtYXI9YygzLDMsMywzKSkKaW1hZ2UucGxvdChsb24sIGxhdCwgZmxpcHBlZF9maXJzdF9ncHBfc2xpY2UsIGNvbD12aXJpZGlzKDI1NiksIHhsYWI9IiIsIHlsYWI9IiIsIG1haW49Z3BwX25hbWUkdmFsdWUsIGxlZ2VuZC5sYWI9Z3BwX3VuaXRzJHZhbHVlLCBsZWdlbmQubGluZT00LCBsZWdlbmQubWFyPTcpCm1hcChkYXRhYmFzZSA9ICd3b3JsZCcsIGFkZCA9IFQsIGx3ZD0xLjUpCmBgYAoKIyMgU2F2aW5nIHBsb3RzIApGaW5hbGx5LCB5b3UgbWlnaHQgd2VsbCB3YW50IHRvIHNhdmUgdGhlIHBsb3RzIGZvciBsYXRlciB1c2UsIHVzZSBpbiByZXBvcnRzIG9yIHByZXNlbnRhdGlvbnMsIG9yIHdoYXRldmVyLiBBbnkgcGxvdCAoZS5nLiBzY2F0dGVyIGdyYXBoLCBwaWUgY2hhcnQsIG1hcCwgZXRjKSBjYW4gYmUgc2F2ZWQgaW4gUi4gCgpTYXZpbmcgcGxvdHMgaW4gUiByZXF1aXJlcyAyIGV4dHJhIGxpbmVzIG9mIGNvZGUuIEZpcnN0LCB5b3Ugb3BlbiBhIGdyYXBoaWMgZGV2aWNlIHVzaW5nIG9uZSBvZiB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uczogcG5nKCksIGpwZXAoKSwgcGRmKCksIHRpZmYoKS4gQWZ0ZXIgdGhpcywgeW91IHJ1biB5b3VyIG5vcm1hbCBwbG90dGluZyBjb2RlLiBUaGVuIGFmdGVyIHlvdXIgcGxvdHRpbmcgY29kZSwgYWRkIHRoZSBsaW5lIGRldi5vZmYoKSB0byBjbG9zZSB0aGUgZ3JhcGhpY3MgZGV2aWNlLiAKCk5vdGUgdGhhdCBpZiB5b3Ugc2F2ZSBwbG90cyBhcyBhIHBkZiwgeW91IGNhbiB0aGVuIG9wZW4gdGhlbSBpbiBJbmtzY2FwZSBvciBBZG9iZSBJbGx1c3RyYXRvciB0byBtb2RpZnkgZnVydGhlci4gCmBgYHtyfQpwbmcoImdwcF9tYXAucG5nIiwgd2lkdGg9MTAsIGhlaWdodD01LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKcGFyKG1hcj1jKDMsMywzLDMpKQppbWFnZS5wbG90KGxvbiwgbGF0LCBmbGlwcGVkX2ZpcnN0X2dwcF9zbGljZSwgY29sPXBsYXNtYSgyNTYpLCB4bGFiPSIiLCB5bGFiPSIiLCBtYWluPWdwcF9uYW1lJHZhbHVlLCBsZWdlbmQubGFiPWdwcF91bml0cyR2YWx1ZSwgbGVnZW5kLmxpbmU9NCwgbGVnZW5kLm1hcj03KQptYXAoZGF0YWJhc2UgPSAnd29ybGQnLCBhZGQgPSBULCBsd2Q9MS41KQpkZXYub2ZmKCkKYGBgCgpOb3RlIHlvdSBuZWVkIHRvIHJ1biB0aGUgd2hvbGUgY2h1bmsgd2l0aCAqQ3RybCtTaGlmdCtFbnRlciosIFJ1bm5pbmcgaXQgbGluZSBieSBsaW5lIHdpdGggKkN0cmwrRW50ZXIqIHdpbGwgY2F1c2UgcHJvYmxlbXMuIEFmdGVyIHJ1bm5pbmcgdGhpcyBjb2RlLCB5b3Ugc2hvdWxkIGdldCBhbiBvdXRwdXQgc2F5aW5nIApudWxsIGRldmljZQogICAgICAgICAgMQp3aGljaCBtZWFucyBpdCBoYXMgc3VjZXNzZnVsbHkgY2xvc2VkIHRoZSBncmFwaGljcyBkZXZpY2UuIFlvdSBzaG91bGQgdGhlbiBoYXZlIGEgcGxvdCBzYXZlZCBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LiAKCgojIyBFbmQgb2YgTm90ZWJvb2sgMgpXZWxsIGRvbmUgZm9yIHJlYWNoaW5nIHRoZSBlbmQgb2YgTm90ZWJvb2sgMi4gSSdtIGdlbnVpbmVseSBwcm91ZCBvZiB5b3UgLSB0aGF0IHdhcyBhIGxvdCBvZiBjb2RlLiBCdXQgeW91IGdvdCB0aHJvdWdoIGl0LCBhbmQgaWYgeW91IHVuZGVyc3Rvb2QgZXZlbiBoYWxmIG9mIGl0IHlvdSBjYW4gbm93IGRvIGEgaHVnZSBhbW91bnQuIEFzIHdpdGggdGhlIGxhc3QgUiBub3RlYm9vaywgSSByZWNvbW1lbmQgZ29pbmcgdGhyb3VnaCB0aGlzIGNvZGUgYXQgbGVhc3Qgb25lIG1vcmUgdGltZSBhbmQgcG9zc2libHkgc2V2ZXJhbCB0aW1lcy4gQW5kIGRlZmluaXRlbHkgc3RhcnQgdHJ5aW5nIHRvIHBsYXkgd2l0aCB0aGUgY29kZS4gQ2hhbmdlIGJpdHMgb24gZWFjaCBsaW5lIHRvIHNlZSB3aGF0IGhhcHBlbnMuIEdldCB1c2VkIHRvIFIgdGhyb3dpbmcgZXJyb3IgbWVzc2FnZXMsIGFuZCBnZXQgdXNlZCB0byBnb29nbGluZyBob3cgdG8gZG8gdGhpbmdzLiBGcm9tIHJlYWRpbmcgdGhlc2UgZ3VpZGVzIHlvdSB3aWxsIG5vdyBoYXZlIGEgZ29vZCBpZGVhIG9mIHRoZSBzb3J0cyBvZiB0aGluZ3MgeW91IGNhbiBkbyB3aXRoIFIuCgoKCmBgYHtyfQprbml0cjo6cHVybCgiUm1kX3NjcmlwdF8yX05ldENERi5SbWQiKQpgYGA=